/*******************************************************************************
 * Copyright (c) 2007 University of Oslo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University of Oslo - initial API and implementation
 *******************************************************************************/

package com.cea.papyrus.diagram.sequence.sedi.editor.message;

import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.createUniqueName;
import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.getCoveredBys;
import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.getCovereds;
import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.getFragments;
import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.getPackagedElements;

import java.util.List;

import org.eclipse.uml2.uml.Event;
import org.eclipse.uml2.uml.Interaction;
import org.eclipse.uml2.uml.InteractionFragment;
import org.eclipse.uml2.uml.InteractionOperand;
import org.eclipse.uml2.uml.Lifeline;
import org.eclipse.uml2.uml.MessageEnd;
import org.eclipse.uml2.uml.MessageOccurrenceSpecification;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Namespace;
import org.eclipse.uml2.uml.OccurrenceSpecification;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.ReceiveOperationEvent;
import org.eclipse.uml2.uml.ReceiveSignalEvent;
import org.eclipse.uml2.uml.SendOperationEvent;
import org.eclipse.uml2.uml.SendSignalEvent;
import org.eclipse.uml2.uml.Signal;
import org.eclipse.uml2.uml.UMLFactory;

import com.cea.papyrus.diagram.sequence.sedi.model.command.EventPosition;
import com.cea.papyrus.diagram.sequence.sedi.model.command.LifelineMessageEndPosition;
import com.cea.papyrus.diagram.sequence.sedi.model.command.MessageEndPosition;

/**
 * Assists in handling of messages on Lifelines. Instances of this class is created by
 * calling {@link MessageHelper#select(Object)}.
 * 
 * @author frankd
 */
class LifelineMessageHelper extends MessageHelper {

    @Override
    public Interaction getInteraction(MessageEndPosition position) {
        LifelineMessageEndPosition lpos = getLifelinePosition(position);
        return lpos.getLifeline().getInteraction();
    }

    @Override
    public MessageEnd createMessageEnd(MessageEndPosition position, boolean send) {
        final MessageOccurrenceSpecification eo =
                UMLFactory.eINSTANCE.createMessageOccurrenceSpecification();
        addMessageEnd(position, eo, send);
        return eo;
    }

    @Override
    public void addMessageEnd(final MessageEndPosition position, final MessageEnd end,
            final boolean send) {
        final OccurrenceSpecification eo = (OccurrenceSpecification) end;

        List<InteractionFragment> fragments = getFragmentsOf(position.getOwner());
        eo.setName(createUniqueName(fragments, send ? "Send" : "Receive_"));
        fragments.add(eo);

        LifelineMessageEndPosition lpos = getLifelinePosition(position);
        Lifeline lifeline = lpos.getLifeline();
        getCoveredBys(lifeline).add(
                (lpos.getLifelinePosition() == null) ? 0 : lifeline.getCoveredBys().indexOf(
                        lpos.getLifelinePosition()) + 1, eo);
    }

    @Override
    public void sendMessage(MessageEnd end, NamedElement signature) {
        final org.eclipse.uml2.uml.Package owner = end.getNearestPackage();
        Event event = null;

        if (signature instanceof Operation) {
            SendOperationEvent sendEvent = UMLFactory.eINSTANCE.createSendOperationEvent();
            sendEvent.setOperation((Operation) signature);
            sendEvent.setName(createUniqueName(owner, "SendOperationEvent_"));
            event = sendEvent;
        } else if (signature instanceof Signal) {
            SendSignalEvent sendEvent = UMLFactory.eINSTANCE.createSendSignalEvent();
            sendEvent.setSignal((Signal) signature);
            sendEvent.setName(createUniqueName(owner, "SendSignalEvent_"));
            event = sendEvent;
        }

        if (event != null) {
            getPackagedElements(owner).add(event);
            ((OccurrenceSpecification) end).setEvent(event);
        }
    }

    @Override
    public void receiveMessage(MessageEnd end, NamedElement signature) {
        final Namespace owner = end.getNearestPackage();
        Event event = null;

        if (signature instanceof Operation) {
            ReceiveOperationEvent recEvent = UMLFactory.eINSTANCE.createReceiveOperationEvent();
            recEvent.setOperation((Operation) signature);
            recEvent.setName(createUniqueName(owner, "ReceiveOperationEvent_"));
            event = recEvent;
        } else if (signature instanceof Signal) {
            ReceiveSignalEvent recEvent = UMLFactory.eINSTANCE.createReceiveSignalEvent();
            recEvent.setSignal((Signal) signature);
            recEvent.setName(createUniqueName(owner, "ReceiveSignalEvent_"));
            event = recEvent;
        }

        if (event != null) {
            getPackagedElements(end.getNearestPackage()).add(event);
            ((OccurrenceSpecification) end).setEvent(event);
        }
    }

    @Override
    public MessageEndPosition removeMessageEnd(final MessageEnd end) {
        final OccurrenceSpecification eo = (OccurrenceSpecification) end;
        final Lifeline lifeline = getCovereds(eo).isEmpty() ? null : getCovereds(eo).get(0);
        final int index = (lifeline != null) ? lifeline.getCoveredBys().indexOf(eo) : -1;

        final LifelineMessageEndPosition lpos = new LifelineMessageEndPosition();
        lpos.setLifeline(lifeline);
        lpos.setLifelinePosition(index > 0 ? getCoveredBys(lifeline).get(index - 1) : null);
        lpos.setOwner((NamedElement) eo.getOwner());

        eo.getCovereds().clear();

        if (eo.getOwner() instanceof Interaction) {
            Interaction i = (Interaction) eo.getOwner();
            i.getFragments().remove(eo);
        } else if (eo.getOwner() instanceof InteractionOperand) {
            InteractionOperand io = (InteractionOperand) eo.getOwner();
            io.getFragments().remove(eo);
        } else
            throw new IllegalArgumentException("Owner not supported: " + eo.getOwner());

        return lpos;
    }

    @Override
    public EventPosition removeMessage(MessageEnd end) {
        EventPosition pos;
        OccurrenceSpecification occ = (OccurrenceSpecification) end;
        if (occ.getEvent() == null)
            pos = null;
        else {
            pos = new EventPosition(occ.getEvent());
            pos.removeEvent();
            occ.setEvent(null);
        }
        return pos;
    }

    private LifelineMessageEndPosition getLifelinePosition(MessageEndPosition position) {
        return (LifelineMessageEndPosition) position;
    }

    private List<InteractionFragment> getFragmentsOf(NamedElement namespace) {
        if (namespace instanceof Interaction)
            return getFragments((Interaction) namespace);
        else if (namespace instanceof InteractionOperand)
            return getFragments((InteractionOperand) namespace);
        else
            throw new IllegalArgumentException("Owner not supported: " + namespace);
    }
}